/**
 * 
 */
package com.vivid.dynamics.oknow.test.dao;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.vivid.dynamics.junit.impl.SpringHibernateSupportJUnitTemplate;
import com.vivid.dynamics.oknow.dao.IBookmarkDAO;
import com.vivid.dynamics.oknow.dao.IPermissionDAO;
import com.vivid.dynamics.oknow.dao.IUserDAO;
import com.vivid.dynamics.oknow.model.Bookmark;
import com.vivid.dynamics.oknow.model.Permission;
import com.vivid.dynamics.oknow.model.User;

/**
 *
 * @author jason
 * created at:: Aug 11, 2011 10:44:14 AM
 */
public class BookmarkNPermissionTestCase extends SpringHibernateSupportJUnitTemplate {
	/**
	 * IUserDAO, check the config under config/spring-config.xml
	 */
	private @Autowired IUserDAO userDAO;
	/**
	 * IBookmarkDAO, check the config under config/spring-config.xml
	 */
	private @Autowired IBookmarkDAO bookmarkDAO;
	/**
	 * IPermissionDAO, check the config under config/spring-config.xml
	 */
	private @Autowired IPermissionDAO permissionDAO;
	
	/**
	 * ctor
	 */
	public BookmarkNPermissionTestCase() {
		super(BookmarkNPermissionTestCase.class, true);
	}
	
	User userA=null;
	Serializable userAKey=null;

	Bookmark markA=null;
	Bookmark markB=null;
	Serializable markAKey=null;
	Serializable markBKey=null;
	
	Permission perA01=null;
	Permission perA02=null;
	Permission perB01=null;
	Permission perB02=null;
	Serializable perA01Key=null;
	Serializable perA02Key=null;
	Serializable perB01Key=null;
	Serializable perB02Key=null;
	
	/**
	 * method(s) to create default users, bookmarks, permission for testing
	 * <ul>
	 * 	<li>new and persists a User</li>
	 * 	<li>create Bookmarks and Permissions; associate them with the above User</li>
	 * 	<li>persist the above bookmarks and permissions (save to db)</li>
	 * 	<li>solve the hashCode() hell on Set collections</li>
	 * 	<li>update the User with all the associations done above</li>
	 * </ul>
	 * 
	 * @author jason
	 * created at:: Aug 9, 2011  12:06:58 PM
	 */
	protected void createDefaultEntities() {
		Timestamp aCurDate=new Timestamp(new Date().getTime());
		
		/*
		 * user
		 */
		userA=new User("userA", "def", false, "hire", "a@abcAA.com", null);
		this.userAKey=this.userDAO.newUser(userA);
		
		/*
		 * bkmark , perm, + associations
		 */
		this.markA=new Bookmark("bmarkA", "bookmarkA for testing", aCurDate, 1, 1, 1d, 1, 1, 1d, "bookmark;testing", true, "http://www.google.com/?q=bookmarkA");
		this.markB=new Bookmark("bmarkB", "bookmarkB for testing", aCurDate, 1, 1, 1d, 1, 1, 1d, "bookmark;testing;B", true, "http://www.google.com/?q=bookmarkB");
		
		this.perA01=new Permission("grp-101:2", "bmarkA's 101 write", "bmarks for A");
		this.perA02=new Permission("all:1", "bmarkA's all read", "bmarks for A");
		this.perB01=new Permission("grp-231:3", "bmarkB's 231 read+write", "bmarks for B");
		this.perB02=new Permission("all:1", "bmarkB's all read", null);
		
		/*
		 * list... no hashCode hell problem
		 */
		this.userA.addBookmark(markA);
		this.userA.addBookmark(markB);
		this.markAKey=this.bookmarkDAO.newBookmark(markA);
		this.markBKey=this.bookmarkDAO.newBookmark(markB);
		/*
		 * set... hashCode hell problem...
		 */
		this.userA.addPermission(this.perA01);
		this.userA.addPermission(this.perA02);
		this.userA.addPermission(this.perB01);
		this.userA.addPermission(this.perB02);
		this.perA01Key=this.permissionDAO.newPermission(perA01);
		this.perA02Key=this.permissionDAO.newPermission(perA02);
		this.perB01Key=this.permissionDAO.newPermission(perB01);
		this.perB02Key=this.permissionDAO.newPermission(perB02);
		
		/*
		 * association between bookmark and permissions
		 * since the permission and bookmark objects are 
		 * fully persisted (id field is non 0); there is 
		 * no hashCode() hell for this case
		 */
		this.markA.addPermission(perA01);
		this.markA.addPermission(perA02);
		this.markB.addPermission(perB01);
		this.markB.addPermission(perB02);
		this.bookmarkDAO.updateBookmark(markA);
		this.bookmarkDAO.updateBookmark(markB);
		
		/*
		 * solving the hashCode hell for the Set<Permission>
		 */
		this.userA.getPermissions().clear();
		this.userA.addPermission(this.perA01);
		this.userA.addPermission(this.perA02);
		this.userA.addPermission(this.perB01);
		this.userA.addPermission(this.perB02);
		
		this.userDAO.updateUser(userA);
	}
	
	/**
	 * <ul>
	 * 	<li>create 2 Bookmarks and create 2 permission(s) for them each {bookmarkA, bookmarkB} {perA01, perA02, perB01, perB02}</li>
	 * </ul>
	 * 
	 * @author jason
	 * created at:: Aug 11, 2011  10:52:27 AM
	 */
	@Test
	public void doTest001() {
		User aUsrA=null;
		Bookmark aMarkA=null;
		Bookmark aMarkB=null;
		Permission aPerA01=null;
		Permission aPerA02=null;
		Permission aPerB01=null;
		Permission aPerB02=null;
		
		this.sb.append("\r\n************************************************\r\n").
		    append("*\tstart {BookmarkNPermissionTestCase.doTest001}\t*\r\n").
			append("************************************************\r\n\r\n");
		
		try {
			this.createDefaultEntities();
			
			/*
			 *  this is a MUST, as the Permission/Bookmark 
			 *  entities aren't updated yet (the hashcode is not valid 
			 *  at the first insertion; since still "0" or null
			 */
			aMarkA=this.bookmarkDAO.getBookmarkByKey((Integer) this.markAKey);
			aMarkB=this.bookmarkDAO.getBookmarkByKey((Integer) this.markBKey);
			aPerA01=this.permissionDAO.getPermissionByKey((Integer) this.perA01Key);
			aPerA02=this.permissionDAO.getPermissionByKey((Integer) this.perA02Key);
			aPerB01=this.permissionDAO.getPermissionByKey((Integer) this.perB01Key);
			aPerB02=this.permissionDAO.getPermissionByKey((Integer) this.perB02Key);
			aUsrA=this.userDAO.getUserByKey((Integer) this.userAKey);
			
			this.sb.append(aMarkA).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aMarkA.getPermissions());
			this.sb.append("\r\n\t").append(aMarkB).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aMarkB.getPermissions());
			this.sb.append("\r\n\r\nPermissions~ :: \r\n\t").append(aPerA01).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerA01.getBookmarks());
			this.sb.append("\r\n\t").append(aPerA02).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerA02.getBookmarks());
			this.sb.append("\r\n\t").append(aPerB01).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerB01.getBookmarks());
			this.sb.append("\r\n\t").append(aPerB02).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerB02.getBookmarks());
			this.sb.append("\r\n\r\n");
			
			this.sb.append("UsrA contains PerA01? :: ").append(aUsrA.getPermissions().contains(aPerA01)).append("\r\n");
			this.sb.append("UsrA contains PerA02? :: ").append(aUsrA.getPermissions().contains(aPerA02)).append("\r\n");
			this.sb.append("UsrA contains PerB01? :: ").append(aUsrA.getPermissions().contains(aPerB01)).append("\r\n");
			this.sb.append("UsrA contains PerB02? :: ").append(aUsrA.getPermissions().contains(aPerB02)).append("\r\n");
			this.sb.append("UsrA contains MarkA? :: ").append(aUsrA.getBookmarks().contains(aMarkA)).append("\r\n");
			this.sb.append("UsrA contains MarkB? :: ").append(aUsrA.getBookmarks().contains(aMarkB)).append("\r\n\r\n");
			
			this.tx.flush();
			
		} catch (RuntimeException re) {
			this.sb.append("**************************************\r\n").
				append(re.toString()).
				append("\r\n**************************************\r\n");
			re.printStackTrace(System.out);
			throw re;
		}
	}
	
	/**
	 * update the bookmark Permission {save on Bookmark/Owner side}
	 * 
	 * @author jason
	 * created at:: Aug 11, 2011  3:53:17 PM
	 */
	@Test
	public void doTest002() {
		Bookmark aMarkA=null;
		Bookmark aMarkB=null;
		Permission aPerA01=null;
		Permission aPerA02=null;
		Permission aPerB01=null;
		Permission aPerB02=null;
		
		this.sb.append("\r\n************************************************\r\n").
		    append("*\tstart {BookmarkNPermissionTestCase.doTest002}\t*\r\n").
			append("************************************************\r\n\r\n");
		
		try {
			this.createDefaultEntities();
			
			aMarkA=this.markA;
			aMarkB=this.markB;
			aPerA01=this.perA01;
			aPerA02=this.perA02;
			aPerB01=this.perB01;
			aPerB02=this.perB02;
			
			/*
			 * 2) update the bookmark Permission {save on Bookmark/Owner side}
			 */
			this.sb.append("2) update the bookmark Permission {save on Bookmark/Owner side} SHOULD work on both sides; add date to descr~:\r\n\t");
			if (StringUtils.isNotBlank(aPerA01.getDescr())) {
				aPerA01.setDescr("{"+aMarkA.getCreateDate()+"} ### "+aPerA01.getDescr());
			}
			if (StringUtils.isNotBlank(aPerB01.getDescr())) {
				aPerB01.setDescr("{"+aMarkB.getCreateDate()+"} &^% "+aPerB01.getDescr());
			}
			/*
			 * update using both Bookmark (A) side
			 * and Permission (B01, B02) side
			 */
			this.bookmarkDAO.updateBookmark(aMarkA);
			//this.bookmarkDAO.updateBookmark(aMarkB);
			this.permissionDAO.updatePermission(aPerB01);
			this.permissionDAO.updatePermission(aPerB02);
			
			// re-query
			aMarkA=this.bookmarkDAO.getBookmarkByKey(aMarkA.getId());
			aMarkB=this.bookmarkDAO.getBookmarkByKey(aMarkB.getId());
			aPerA01=this.permissionDAO.getPermissionByKey(aPerA01.getId());
			aPerA02=this.permissionDAO.getPermissionByKey(aPerA02.getId());
			aPerB01=this.permissionDAO.getPermissionByKey(aPerB01.getId());
			aPerB02=this.permissionDAO.getPermissionByKey(aPerB02.getId());
			
			this.sb.append(aMarkA).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aMarkA.getPermissions());
			this.sb.append("\r\n\t").append(aMarkB).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aMarkB.getPermissions());
			this.sb.append("\r\n\r\nPermissions~ :: \r\n\t").append(aPerA01).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerA01.getBookmarks());
			this.sb.append("\r\n\t").append(aPerA02).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerA02.getBookmarks());
			this.sb.append("\r\n\t").append(aPerB01).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerB01.getBookmarks());
			this.sb.append("\r\n\t").append(aPerB02).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerB02.getBookmarks());
			this.sb.append("\r\n\r\n");
			
			this.tx.flush();
			
		} catch (RuntimeException re) {
			this.sb.append("**************************************\r\n").
				append(re.toString()).
				append("\r\n**************************************\r\n");
			re.printStackTrace(System.out);
			throw re;
		}
	}
	
	/**
	 * swap the contents of the Bookmarks; swap all bookmarkB's permissions to bookmarkA
	 * 
	 * @author jason
	 * created at:: Aug 11, 2011  4:07:23 PM
	 */
	@Test
	public void doTest003() {
		Bookmark aMarkA=null;
		Bookmark aMarkB=null;
		Permission aPerA01=null;
		Permission aPerA02=null;
		Permission aPerB01=null;
		Permission aPerB02=null;
		
		this.sb.append("\r\n************************************************\r\n").
		    append("*\tstart {BookmarkNPermissionTestCase.doTest003}\t*\r\n").
			append("************************************************\r\n\r\n");
		
		try {
			this.createDefaultEntities();
			
			aMarkA=this.markA;
			aMarkB=this.markB;
			aPerA01=this.perA01;
			aPerA02=this.perA02;
			aPerB01=this.perB01;
			aPerB02=this.perB02;
			
			this.sb.append("3) swap contents of Bookmark(s); transfer all permissions from BookmarkB to BookmarkA::\r\n\r\n");
			/*
			 * MUST use association method... or unexpected results might happen
			 */
			aMarkB.removePermission(aPerB01);
			aMarkB.removePermission(aPerB02);
			this.bookmarkDAO.updateBookmark(aMarkB);
			
			// re-query
			aMarkA=this.bookmarkDAO.getBookmarkByKey(aMarkA.getId());
			aMarkB=this.bookmarkDAO.getBookmarkByKey(aMarkB.getId());
			aPerA01=this.permissionDAO.getPermissionByKey(aPerA01.getId());
			aPerA02=this.permissionDAO.getPermissionByKey(aPerA02.getId());
			aPerB01=this.permissionDAO.getPermissionByKey(aPerB01.getId());
			aPerB02=this.permissionDAO.getPermissionByKey(aPerB02.getId());
			
			this.sb.append(aMarkA).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aMarkA.getPermissions());
			this.sb.append("\r\n\t").append(aMarkB).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aMarkB.getPermissions());
			this.sb.append("\r\n\r\nPermissions~ :: \r\n\t").append(aPerA01).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerA01.getBookmarks());
			this.sb.append("\r\n\t").append(aPerA02).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerA02.getBookmarks());
			this.sb.append("\r\n\t").append(aPerB01).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerB01.getBookmarks());
			this.sb.append("\r\n\t").append(aPerB02).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerB02.getBookmarks());
			this.sb.append("\r\n\r\n");
			
			/*
			 * add the removed permission(s) to bookmarkA
			 */
			aMarkA.addPermission(aPerB01);
			aMarkA.addPermission(aPerB02);
			this.bookmarkDAO.updateBookmark(aMarkA);
			this.userDAO.updateUser(this.userA);
			
			aMarkA=this.bookmarkDAO.getBookmarkByKey(aMarkA.getId());
			aMarkB=this.bookmarkDAO.getBookmarkByKey(aMarkB.getId());
			aPerA01=this.permissionDAO.getPermissionByKey(aPerA01.getId());
			aPerA02=this.permissionDAO.getPermissionByKey(aPerA02.getId());
			aPerB01=this.permissionDAO.getPermissionByKey(aPerB01.getId());
			aPerB02=this.permissionDAO.getPermissionByKey(aPerB02.getId());
			
			this.sb.append(aMarkA).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aMarkA.getPermissions());
			this.sb.append("\r\n\t").append(aMarkB).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aMarkB.getPermissions());
			this.sb.append("\r\n\r\nPermissions~ :: \r\n\t").append(aPerA01).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerA01.getBookmarks());
			this.sb.append("\r\n\t").append(aPerA02).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerA02.getBookmarks());
			this.sb.append("\r\n\t").append(aPerB01).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerB01.getBookmarks());
			this.sb.append("\r\n\t").append(aPerB02).append("\r\n\t\t");
			this.createToStringForCollections(this.sb, aPerB02.getBookmarks());
			this.sb.append("\r\n\r\n");
			
			/*
			 * final.. from User point of view...
			 */
			this.sb.append("Final) query the UserA and it's children~ :\r\n\t");
			this.userA=this.userDAO.getUserByKey((Integer) this.userAKey);
			this.sb.append(this.userA).append("\r\n\t\tBookmarks:: ");
			this.createToStringForCollections(this.sb, this.userA.getBookmarks());
			this.sb.append("\r\n\t\tPermissions:: ");
			this.createToStringForCollections(this.sb, this.userA.getPermissions());
			this.sb.append("\r\n\r\n:now remove bookmarkA (means... no more bookmark+permissions associations after delete, since all permissions are under A now) \r\n\r\n");
			
			if (this.markA!=null && this.markA.getId()!=0) {
				Assert.assertTrue("3) should have permissions under MarkA~~~", this.markA.getPermissions()!=null);
				Permission[] aPerArr=new Permission[this.markA.getPermissions().size()]; 
				this.markA.getPermissions().toArray(aPerArr);
				/*
				 * do not use a Iter.. in this case... since you are changing
				 * the iterator's structure... some items missing, which will
				 * cause an Exception... concurrentModificationException
				 */
				for (Permission aPer:aPerArr) {
					this.markA.removePermission(aPer);
				}
				/*
				 * update Bookmark + Permission associations
				 */
				this.bookmarkDAO.updateBookmark(markA);
				/*
				 * update the User + Bookmark associations
				 */
				this.userA.removeBookmark(this.markA);
				this.bookmarkDAO.deleteBookmark(markA);
				this.userDAO.updateUser(this.userA);
			}
			// re-query
			aMarkA=this.bookmarkDAO.getBookmarkByKey(aMarkA.getId());
			aMarkB=this.bookmarkDAO.getBookmarkByKey(aMarkB.getId());
			aPerA01=this.permissionDAO.getPermissionByKey(aPerA01.getId());
			aPerA02=this.permissionDAO.getPermissionByKey(aPerA02.getId());
			aPerB01=this.permissionDAO.getPermissionByKey(aPerB01.getId());
			aPerB02=this.permissionDAO.getPermissionByKey(aPerB02.getId());
			
			this.userA=this.userDAO.getUserByKey((Integer) this.userAKey);
			this.sb.append(this.userA).append("\r\n\t\tBookmarks:: ");
			this.createToStringForCollections(this.sb, this.userA.getBookmarks());
			this.sb.append("\r\n\t\tPermissions:: ");
			this.createToStringForCollections(this.sb, this.userA.getPermissions());
			this.sb.append("\r\n\r\n");
			
			this.tx.flush();
			
		} catch (RuntimeException re) {
			this.sb.append("**************************************\r\n").
				append(re.toString()).
				append("\r\n**************************************\r\n");
			re.printStackTrace(System.out);
			throw re;
		}			
	}
	
	/**
	 * Test(s) on Bookmark and Permission associations
	 * <ul>
	 * 	<li>create 2 Bookmarks and create 2 permission(s) for them each {bookmarkA, bookmarkB} {perA01, perA02, perB01, perB02}</li>
	 * 	<li></li>
	 * 	<li>update the bookmark Permission {save on Permission side} ==> would WORK this is not related to adding/removing associated entities, just changing the inner values</li>
	 * 	<li>change bookmark permissions; add this, remove that etc</li>
	 * </ul>
	 * <p/> 
	 * 
	 * @author jason
	 * created at:: Aug 5, 2011  3:59:20 PM
	 * @deprecated
	 */
	//@Test
	public void doBookmarkNPermissionTest001() {
		User aUsrA=null;
		Bookmark aMarkA=null;
		Bookmark aMarkB=null;
		Permission aPerA01=null;
		Permission aPerA02=null;
		Permission aPerB01=null;
		Permission aPerB02=null;
		Serializable aUsrAKey=null;
		Serializable aMarkAKey=null;
		Serializable aMarkBKey=null;
		Serializable aPerA01Key=null;
		Serializable aPerA02Key=null;
		Serializable aPerB01Key=null;
		Serializable aPerB02Key=null;
		Timestamp aCurDate=new Timestamp(new Date().getTime());
		
		try {
			this.sb.append("\r\n************************************************\r\n").
			    append("*\tstart {doBookmarkNPermissionTest001}\t*\r\n").
				append("************************************************\r\n\r\n");
			this.sb.append("1) create 1 User, 2 Bookmarks and create 2 permission(s) for them each {bookmarkA, bookmarkB} {perA01, perA02, perB01, perB02}:\r\nBookmark::\r\n\t");
			
			aUsrA=new User("userA", "joestar", false, "RootAdmin->default", "abc@abc.com", null);
			aMarkA=new Bookmark("bmarkA", "bookmarkA for testing", aCurDate, 1, 1, 1d, 1, 1, 1d, "bookmark;testing", true, "http://www.google.com/?q=bookmarkA");
			aMarkB=new Bookmark("bmarkB", "bookmarkB for testing", aCurDate, 1, 1, 1d, 1, 1, 1d, "bookmark;testing;B", true, "http://www.google.com/?q=bookmarkB");
			aPerA01=new Permission("grp-101:2", "bmarkA's 101 write", "bmarks for A");
			aPerA02=new Permission("all:1", "bmarkA's all read", "bmarks for A");
			aPerB01=new Permission("grp-231:3", "bmarkB's 231 read+write", "bmarks for B");
			aPerB02=new Permission("all:1", "bmarkB's all read", null);
			/*
			 * persist order
			 * a) persist User
			 * b) permission persistent (associate with UserA)
			 * c) owner... {bookmark} (associate with UserA)
			 * d) set association
			 * 
			 * PS. no matter what
			 * 1) new all entities first (in this case... parent must exists before children, ie. USER must be there, then Permission / Bookmark)
			 * 2) 
			 * actually no problem on the logical orders, 
			 * the updated entity will have the PK updated; 
			 * no worries for re-query
			 */
			aUsrAKey=this.userDAO.newUser(aUsrA);
			
			/*
			 * a) add by association(s); however the Id(s) of children not
			 * yet ready... need to re-add later on
			 */
			aUsrA.addPermission(aPerA01);
			aUsrA.addPermission(aPerA02);
			aUsrA.addPermission(aPerB01);
			aUsrA.addPermission(aPerB02);
			
			aUsrA.addBookmark(aMarkA);
			aUsrA.addBookmark(aMarkB);
						
			aPerA01Key=this.permissionDAO.newPermission(aPerA01);
			aPerA02Key=this.permissionDAO.newPermission(aPerA02);
			aPerB01Key=this.permissionDAO.newPermission(aPerB01);
			aPerB02Key=this.permissionDAO.newPermission(aPerB02);
			
			aMarkAKey=this.bookmarkDAO.newBookmark(aMarkA);
			aMarkBKey=this.bookmarkDAO.newBookmark(aMarkB);

			aMarkA.addPermission(aPerA01);
			aMarkA.addPermission(aPerA02);
			aMarkB.addPermission(aPerB01);
			aMarkB.addPermission(aPerB02);
			
			this.bookmarkDAO.updateBookmark(aMarkA);
			this.bookmarkDAO.updateBookmark(aMarkB);
			/*
			 * b) first clear all stacks and add back the updated
			 * entities, then update
			 * 
			 * weird enough but LOGICAL
			 */
			aUsrA.getPermissions().clear();
			aUsrA.getBookmarks().clear();
			aUsrA.getPermissions().add(aPerA01);
			aUsrA.getPermissions().add(aPerA02);
			aUsrA.getPermissions().add(aPerB01);
			aUsrA.getPermissions().add(aPerB02);
			aUsrA.getBookmarks().add(aMarkA);
			aUsrA.getBookmarks().add(aMarkB);
			
			this.userDAO.updateUser(aUsrA);
			aUsrA=this.userDAO.getUserByKey((Integer) aUsrAKey);
			
			/*
			 *  the permission List is not up-todate yet... shxt... 
			 *  no idea how to make it updated (update here means the set's hashcode)
			 *  
			 *  the values itself is up-to-date for sure
			 */
			//this.userDAO.updateUser(aUsrA);
			
			// re-query
			/*
			 *  this is a MUST, as the Permission/Bookmark 
			 *  entities aren't updated yet (the hashcode is not valid 
			 *  at the first insertion; since still "0" or null
			 */
			aMarkA=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkAKey);
			aMarkB=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkBKey);
			aPerA01=this.permissionDAO.getPermissionByKey((Integer) aPerA01Key);
			aPerA02=this.permissionDAO.getPermissionByKey((Integer) aPerA02Key);
			aPerB01=this.permissionDAO.getPermissionByKey((Integer) aPerB01Key);
			aPerB02=this.permissionDAO.getPermissionByKey((Integer) aPerB02Key);
			aUsrA=null;
			aUsrA=this.userDAO.getUserByKey((Integer) aUsrAKey);
			
			this.sb.append(aMarkA).append("\r\n\t\t").append(aMarkA.getPermissions()).
				append("\r\n\t").append(aMarkB).append("\r\n\t\t").append(aMarkB.getPermissions()).
				append("\r\n\r\n\tPermissions~ :: ").
				append(aPerA01).append("\r\n\t\t").append(aPerA01.getBookmarks()).append("\r\n\t").
				append(aPerA02).append("\r\n\t\t").append(aPerA02.getBookmarks()).append("\r\n\t").
				append(aPerB01).append("\r\n\t\t").append(aPerB01.getBookmarks()).append("\r\n\t").
				append(aPerB02).append("\r\n\t\t").append(aPerB02.getBookmarks()).append("\r\n\r\n");
			
			this.sb.append("UsrA contains PerA01? :: ").append(aUsrA.getPermissions().contains(aPerA01)).append("\r\n");
			this.sb.append("UsrA contains PerA02? :: ").append(aUsrA.getPermissions().contains(aPerA02)).append("\r\n");
			this.sb.append("UsrA contains PerB01? :: ").append(aUsrA.getPermissions().contains(aPerB01)).append("\r\n");
			this.sb.append("UsrA contains PerB02? :: ").append(aUsrA.getPermissions().contains(aPerB02)).append("\r\n");
			this.sb.append("UsrA contains MarkA? :: ").append(aUsrA.getBookmarks().contains(aMarkA)).append("\r\n");
			this.sb.append("UsrA contains MarkB? :: ").append(aUsrA.getBookmarks().contains(aMarkB)).append("\r\n\r\n");
			
			/*
			 * 2) update the bookmark Permission {save on Bookmark/Owner side}
			 */
			this.sb.append("2) update the bookmark Permission {save on Bookmark/Owner side}; add date to descr~:\r\n\t");
			if (StringUtils.isNotBlank(aPerA01.getDescr())) {
				aPerA01.setDescr(aCurDate+" ### "+aPerA01.getDescr());
			}
			if (StringUtils.isNotBlank(aPerB01.getDescr())) {
				aPerB01.setDescr(aCurDate+" &^% "+aPerB01.getDescr());
			}
			this.bookmarkDAO.updateBookmark(aMarkA);
			this.bookmarkDAO.updateBookmark(aMarkB);
			
			// re-query
			aMarkA=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkAKey);
			aMarkB=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkBKey);
			aPerA01=this.permissionDAO.getPermissionByKey((Integer) aPerA01Key);
			aPerA02=this.permissionDAO.getPermissionByKey((Integer) aPerA02Key);
			aPerB01=this.permissionDAO.getPermissionByKey((Integer) aPerB01Key);
			aPerB02=this.permissionDAO.getPermissionByKey((Integer) aPerB02Key);
			
			this.sb.append(aMarkA).append("\r\n\t\t").append(aMarkA.getPermissions()).
				append("\r\n\t").append(aMarkB).append("\r\n\t\t").append(aMarkB.getPermissions()).
				append("\r\n\r\n\tPermissions~ :: ").
				append(aPerA01).append("\r\n\t\t").append(aPerA01.getBookmarks()).append("\r\n\t").
				append(aPerA02).append("\r\n\t\t").append(aPerA02.getBookmarks()).append("\r\n\t").
				append(aPerB01).append("\r\n\t\t").append(aPerB01.getBookmarks()).append("\r\n\t").
				append(aPerB02).append("\r\n\t\t").append(aPerB02.getBookmarks()).append("\r\n\r\n");
			
			/*
			 * 3) update the bookmark Permission {save on Permission side} ==> would WORK this is not related to adding/removing associated entities, just changing the inner values
			 */
			this.sb.append("3a) update the bookmark Permission {save on Permission side} ==> would WORK this is not related to adding/removing associated entities, just changing the inner values; add date to descr~:\r\n\t");
			if (StringUtils.isNotBlank(aPerA02.getDescr())) {
				aPerA02.setDescr(aCurDate+" ### "+aPerA02.getDescr());
			}
			if (StringUtils.isNotBlank(aPerB02.getDescr())) {
				aPerB02.setDescr(aCurDate+" &^% "+aPerB02.getDescr());
			}
			this.permissionDAO.updatePermission(aPerA02);
			this.permissionDAO.updatePermission(aPerB02);
			
			// re-query
			aMarkA=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkAKey);
			aMarkB=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkBKey);
			aPerA01=this.permissionDAO.getPermissionByKey((Integer) aPerA01Key);
			aPerA02=this.permissionDAO.getPermissionByKey((Integer) aPerA02Key);
			aPerB01=this.permissionDAO.getPermissionByKey((Integer) aPerB01Key);
			aPerB02=this.permissionDAO.getPermissionByKey((Integer) aPerB02Key);
			
			this.sb.append(aMarkA).append("\r\n\t\t").append(aMarkA.getPermissions()).
				append("\r\n\t").append(aMarkB).append("\r\n\t\t").append(aMarkB.getPermissions()).
				append("\r\n\r\n\tPermissions~ :: ").
				append(aPerA01).append("\r\n\t\t").append(aPerA01.getBookmarks()).append("\r\n\t").
				append(aPerA02).append("\r\n\t\t").append(aPerA02.getBookmarks()).append("\r\n\t").
				append(aPerB01).append("\r\n\t\t").append(aPerB01.getBookmarks()).append("\r\n\t").
				append(aPerB02).append("\r\n\t\t").append(aPerB02.getBookmarks()).append("\r\n\r\n");
			
			/*
			 * 4) change bookmark permissions; add this, remove that etc
			 */
			this.sb.append("4a) change bookmark permissions; add this, remove that etc {change associations swap the permission B02 to bookmarkA}:\r\n:: ");
			this.sb.append("MarkA has PerA01 ? :: ").append(aMarkA.getPermissions().contains(aPerA01)).append("\r\n");
			this.sb.append("MarkA has PerA02 ? :: ").append(aMarkA.getPermissions().contains(aPerA02)).append("\r\n");
			this.sb.append("MarkA has PerB01 ? :: ").append(aMarkA.getPermissions().contains(aPerB01)).append("\r\n");
			this.sb.append("MarkA has PerB02 ? :: ").append(aMarkA.getPermissions().contains(aPerB02)).append("\r\n");

			this.sb.append("MarkB has PerA01 ? :: ").append(aMarkB.getPermissions().contains(aPerA01)).append("\r\n");
			this.sb.append("MarkB has PerA02 ? :: ").append(aMarkB.getPermissions().contains(aPerA02)).append("\r\n");
			this.sb.append("MarkB has PerB01 ? :: ").append(aMarkB.getPermissions().contains(aPerB01)).append("\r\n");
			this.sb.append("MarkB has PerB02 ? :: ").append(aMarkB.getPermissions().contains(aPerB02)).append("\r\n\r\n");
			if (aPerB02!=null) {
				aMarkB.removePermission(aPerB02);
				aMarkA.addPermission(aPerB02);
				
				this.bookmarkDAO.updateBookmark(aMarkB);
				this.bookmarkDAO.updateBookmark(aMarkA);
			}
			/*
			 * not sure why need a flush... to get them right...
			 */
			//this.tx.flush();
			
			// re-query
			aMarkA=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkAKey);
			aMarkB=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkBKey);
			aPerA01=this.permissionDAO.getPermissionByKey((Integer) aPerA01Key);
			aPerA02=this.permissionDAO.getPermissionByKey((Integer) aPerA02Key);
			aPerB01=this.permissionDAO.getPermissionByKey((Integer) aPerB01Key);
			aPerB02=this.permissionDAO.getPermissionByKey((Integer) aPerB02Key);
			
			this.sb.append(aMarkA).append("\r\n\t\t").append(aMarkA.getPermissions()).
				append("\r\n\t").append(aMarkB).append("\r\n\t\t").append(aMarkB.getPermissions()).
				append("\r\n\r\n\tPermissions~ :: ").
				append(aPerA01).append("\r\n\t\t").append(aPerA01.getBookmarks()).append("\r\n\t").
				append(aPerA02).append("\r\n\t\t").append(aPerA02.getBookmarks()).append("\r\n\t").
				append(aPerB01).append("\r\n\t\t").append(aPerB01.getBookmarks()).append("\r\n\t").
				append(aPerB02).append("\r\n\t\t").append(aPerB02.getBookmarks()).append("\r\n");
			this.sb.append("PS. only using the associations + update/delete on the correct side can have the targeted RESULT~~~\r\n\r\n");
			
			/*this.sb.append("4b) update the bookmark Permission {save on Permission side} + remove a Permission from BookmarkB ==> NOT workr~:\r\n\t");
			aMarkB.removePermission(aPerB01);
			*
			 * orphaned data would be introduced... not update on owner's side...
			 *
			this.permissionDAO.updatePermission(aPerB01);
			
			// re-query
			aMarkA=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkAKey);
			aMarkB=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkBKey);
			aPerA01=this.permissionDAO.getPermissionByKey((Integer) aPerA01Key);
			aPerA02=this.permissionDAO.getPermissionByKey((Integer) aPerA02Key);
			aPerB01=this.permissionDAO.getPermissionByKey((Integer) aPerB01Key);
			aPerB02=this.permissionDAO.getPermissionByKey((Integer) aPerB02Key);
			
			this.sb.append(aMarkA).append("\r\n\t\t").append(aMarkA.getPermissions()).
				append("\r\n\t").append(aMarkB).append("\r\n\t\t").append(aMarkB.getPermissions()).
				append("\r\n\r\n\tPermissions~ :: ").
				append(aPerA01).append("\r\n\t\t").append(aPerA01.getBookmarks()).append("\r\n\t").
				append(aPerA02).append("\r\n\t\t").append(aPerA02.getBookmarks()).append("\r\n\t").
				append(aPerB01).append("\r\n\t\t").append(aPerB01.getBookmarks()).append("\r\n\t").
				append(aPerB02).append("\r\n\t\t").append(aPerB02.getBookmarks()).append("\r\n");
			this.sb.append("PS. the Bookmark Side has been updated~ however the Permission side hasn't; this would introduce the problem of ORPHANED DATA~~~\r\n\r\n");
			*/
			this.sb.append("4c) change bookmark permissions; add this, remove that etc {change associations swap the permission B02 to bookmarkA}:\r\n:: ");
			this.sb.append("PerA01 has MarkA ? :: ").append(aPerA01.getBookmarks().contains(aMarkA)).append("\r\n");
			this.sb.append("PerA01 has MarkB ? :: ").append(aPerA01.getBookmarks().contains(aMarkB)).append("\r\n");
			this.sb.append("PerA02 has MarkA ? :: ").append(aPerA02.getBookmarks().contains(aMarkA)).append("\r\n");
			this.sb.append("PerA02 has MarkB ? :: ").append(aPerA02.getBookmarks().contains(aMarkB)).append("\r\n");
			this.sb.append("PerB01 has MarkA ? :: ").append(aPerB01.getBookmarks().contains(aMarkA)).append("\r\n");
			this.sb.append("PerB01 has MarkB ? :: ").append(aPerB01.getBookmarks().contains(aMarkB)).append("\r\n");
			this.sb.append("PerB02 has MarkA ? :: ").append(aPerB02.getBookmarks().contains(aMarkA)).append("\r\n");
			this.sb.append("PerB02 has MarkB ? :: ").append(aPerB02.getBookmarks().contains(aMarkB)).append("\r\n");
			aMarkB.removePermission(aPerB01);
			/*
			 * will be NORMAL this time~
			 */
			this.bookmarkDAO.updateBookmark(aMarkB);
			
			// re-query
			aMarkA=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkAKey);
			aMarkB=this.bookmarkDAO.getBookmarkByKey((Integer) aMarkBKey);
			aPerA01=this.permissionDAO.getPermissionByKey((Integer) aPerA01Key);
			aPerA02=this.permissionDAO.getPermissionByKey((Integer) aPerA02Key);
			aPerB01=this.permissionDAO.getPermissionByKey((Integer) aPerB01Key);
			aPerB02=this.permissionDAO.getPermissionByKey((Integer) aPerB02Key);
			
			this.sb.append(aMarkA).append("\r\n\t\t").append(aMarkA.getPermissions()).
				append("\r\n\t").append(aMarkB).append("\r\n\t\t").append(aMarkB.getPermissions()).
				append("\r\n\r\n\tPermissions~ :: ").
				append(aPerA01).append("\r\n\t\t").append(aPerA01.getBookmarks()).append("\r\n\t").
				append(aPerA02).append("\r\n\t\t").append(aPerA02.getBookmarks()).append("\r\n\t").
				append(aPerB01).append("\r\n\t\t").append(aPerB01.getBookmarks()).append("\r\n\t").
				append(aPerB02).append("\r\n\t\t").append(aPerB02.getBookmarks()).append("\r\n\r\n");
			
			// final... 
			this.sb.append("Final) query the UserA and it's children~ :\r\n\t");
			aUsrA=this.userDAO.getUserByKey((Integer) aUsrAKey);
			this.sb.append(aUsrA).append("\r\n\t\tBookmarks:: ").append(aUsrA.getBookmarks()).
				append("\r\n\t\tPermissions:: ").append(aUsrA.getPermissions()).append("\r\n\r\n:now remove 1 bookmark \r\n\t");
			
			if (aUsrA.getBookmarks()!=null && aUsrA.getBookmarks().size()>0) {
				aUsrA.getBookmarks().remove(0);
				this.userDAO.updateUser(aUsrA);
				aUsrA=this.userDAO.getUserByKey(aUsrA.getId());
			}
			this.sb.append(aUsrA).append("\r\n\t\tBookmarks:: ").append(aUsrA.getBookmarks()).
				append("\r\n\t\tPermissions:: ").append(aUsrA.getPermissions()).append("\r\n");
						
			this.tx.flush();
			
		} catch (RuntimeException re) {
			this.sb.append("**************************************\r\n").
				append(re.toString()).
				append("\r\n**************************************\r\n");
			re.printStackTrace(System.out);
			throw re;
		}
	}
	
}
